// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//

#include "util/time_lut.h"

#include <cstdint>

#include "common/cast_set.h"
#include "vec/runtime/vdatetime_value.h"

namespace doris {
#include "common/compile_check_begin.h"
TimeLUTImpl::TimeLUTImpl() {
    init_time_lut();
}

void TimeLUTImpl::init_time_lut() {
    for (uint16_t y = LUT_START_YEAR; y < LUT_END_YEAR; y++) {
        uint16_t tmp_year = 0;
        for (uint8_t m = 0; m < NUM_MONTHS; m++) {
            for (uint8_t i = 0; i < NUM_DAYS; i++) {
                if (!VecDateTimeValue::check_date(y, m + 1, i + 1)) {
                    // valid date
                    week_table[y - LUT_START_YEAR][m][i] =
                            calc_week(y, m + 1, i + 1, false, false, true, &tmp_year);
                    week_of_year_table[y - LUT_START_YEAR][m][i] =
                            calc_week(y, m + 1, i + 1, true, true, false, &tmp_year);
                    year_week_table[y - LUT_START_YEAR][m][i] = year_week(y, m + 1, i + 1);
                }
            }
        }
    }
}

uint8_t calc_week(uint16_t year, uint8_t month, uint8_t day, bool monday_first, bool week_year,
                  bool first_weekday, uint16_t* to_year) {
    uint64_t day_nr = calc_daynr(year, month, day);
    uint64_t daynr_first_day = calc_daynr(year, 1, 1);
    uint8_t weekday_first_day = calc_weekday(daynr_first_day, !monday_first);

    uint16_t days = 0; // days in year
    *to_year = year;

    // Check weather the first days of this year belongs to last year
    if (month == 1 && day <= (7 - weekday_first_day)) {
        if (!week_year && ((first_weekday && weekday_first_day != 0) ||
                           (!first_weekday && weekday_first_day > 3))) {
            return 0;
        }
        (*to_year)--;
        week_year = true;
        daynr_first_day -= (days = calc_days_in_year(*to_year));
        weekday_first_day = (weekday_first_day + 53 * 7 - days) % 7;
    }

    // How many days since first week
    DCHECK_LE(day_nr - daynr_first_day, 373);
    if ((first_weekday && weekday_first_day != 0) || (!first_weekday && weekday_first_day > 3)) {
        // days in new year belongs to last year.
        days = static_cast<uint16_t>(day_nr - daynr_first_day - (7 - weekday_first_day));
    } else {
        // days in new year belongs to this year.
        days = static_cast<uint16_t>(day_nr - daynr_first_day + weekday_first_day);
    }

    if (week_year && days >= 52 * 7) {
        weekday_first_day = (weekday_first_day + calc_days_in_year(*to_year)) % 7;
        if ((first_weekday && weekday_first_day == 0) ||
            (!first_weekday && weekday_first_day <= 3)) {
            // Belong to next year.
            (*to_year)++;
            return 1;
        }
    }

    return static_cast<uint8_t>(days / 7 + 1);
}

uint16_t calc_days_in_year(uint32_t year) {
    return is_leap(year) ? 366 : 365;
}

uint8_t calc_weekday(uint64_t day_nr, bool is_sunday_first_day) {
    return (day_nr + 5L + (is_sunday_first_day ? 1L : 0L)) % 7;
}

uint32_t year_week(uint16_t yy, uint8_t month, uint8_t day) {
    //not covered by year_week_table, calculate at runtime
    uint16_t to_year = 0;
    // The range of the week in the year_week is 1-53, so the mode WEEK_YEAR is always true.
    uint8_t week = calc_week(yy, month, day, false, true, true, &to_year);
    return to_year * 100 + week;
}
#include "common/compile_check_end.h"
} // namespace doris
